#ifndef __MODULEPLAYER_H__
#define __MODULEPLAYER_H__
#include "ModuleLoader.h"
#include "ModuleMixer.h"

namespace modx {

	typedef struct _SOUND_VARS
	{
		unsigned long			mixFreqPeriod;
		long					samplesUntilMODTick;	// 12-bit fixed-point
		unsigned long			samplesPerMODTick;		// 12-bit fixed-point
	} SOUND_VARS;
	
	typedef struct _MOD_CHANNEL
	{
		unsigned short period;      // Current period of note being played
		unsigned char note;
		unsigned char sample;       // Last sample used on this channel
		char vol;          // Current volume


		unsigned char effect;       // Current effect running (set to 0 row tick if no effect/parameter)
		unsigned char param;        // Current parameter (set to 0 row tick if no effect/parameter)

		char volslideSpeed;
		unsigned char portaSpeed;
		unsigned char tonePortaNote;
		unsigned char tonePortaSpeed;
	} MOD_CHANNEL;

	#define DEFAULT_SPEED	6
	#define DEFAULT_TEMPO	125
	#define PATTERN_ROWS	64

	typedef enum _PLAY_STATE
	{
		PLAY_STATE_STOP,
		PLAY_STATE_PLAY,
		PLAY_STATE_PAUSE,

	} PLAY_STATE;
	
	typedef enum _UPDATE_FLAGS
	{
		UPD_FLG_PLAY_NOTE	= BIT(0),
		UPD_FLG_SET_VOL		= BIT(1),
		UPD_FLG_SET_FREQ	= BIT(2),
	} UPDATE_FLAGS;
	
	typedef struct _UPDATE_VARS
	{
		MOD_CHANNEL		*modChn;
		MixChn	*sndChn;

		unsigned char				note;
		unsigned char				sample;
		unsigned char				effect;
		unsigned char				param;

		unsigned char				updateFlags;
		unsigned char				sampleOffset;

	} UPDATE_VARS;
	
	typedef enum EFFECT_TABLE
	{
		EFFECT_TABLE_ROW,
		EFFECT_TABLE_MID,

		EFFECT_TABLE_NUM,

	} EFFECT_TABLE;
	
	static const UPDATE_VARS defaultVars[EFFECT_TABLE_NUM] =
	{
		{   // MOD_EFFECT_TABLE_ROW
			NULL,						// modChn
			NULL,						// sndChn
			NO_NOTE,				// note
			NO_SAMPLE,				// sample
			0,							// effect
			0,							// param
				// Play note if there is one, but still do nothing if there isn't.
				// Don't set volume or frequency unless something specifically needs it.
			UPD_FLG_PLAY_NOTE,       // updateFlags
									// sampleOffset
			0
		},
		{   // MOD_EFFECT_TABLE_MID
			NULL,						// modChn
			NULL,						// sndChn
			NO_NOTE,				// note
			NO_SAMPLE,				// sample
			0,							// effect
			0,							// param
				// Don't do anything unless something specifically needs it
			0,							// updateFlags
			0							// sampleOffset
		}
	};

	class ModulePlayer {
	
	public:
		ModulePlayer();
		~ModulePlayer();
		
		bool useMixer(ModuleMixer* mixerToUse);
		bool useModule(Module* moduleToUse);
		
		void play();
		void stop();
		void pause();
		
		unsigned long render(short out[], unsigned long nSamples);
		void setTempo(long lTempo);
		
	private:
		inline unsigned short getSampleNotePeriod(unsigned char note, unsigned char sample);
		bool seek(unsigned long order, unsigned long row);
		void update();
		void processRow();
		void updateEffects();
		void handleUpdateFlags(UPDATE_VARS *vars);
		void playNote(UPDATE_VARS *vars);
		
		unsigned long volSlide(unsigned long volume, long slide);
		long pitchSlide(long period, long slide);
		
		void volSlideRow(UPDATE_VARS *vars);
		void volSlideTick(UPDATE_VARS *vars);
		void tonePortaRow(UPDATE_VARS *vars);
		void tonePortaTick(UPDATE_VARS *vars);
		
		void rowEffects(UPDATE_VARS *vars);
		void tickEffects(UPDATE_VARS *vars);
		
		Module* m_pModule;
		ModuleMixer* m_pMixer;
		
		
		SOUND_VARS sndVars;
		std::vector<ModuleNote> *rowPtr;
		PLAY_STATE state;

		unsigned char speed;
		unsigned char tick;

		unsigned char curRow;

		unsigned char orderCount;
		unsigned char curOrder;
		
		char patDelay;
		
		unsigned char nextOrder;
		unsigned char breakRow;

		unsigned char tempo;
		MOD_CHANNEL *channel;
	};
}
#endif